// Includes
#include <iostream>

#include "../utils/cuPrintf.cu"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "../utils/Random.cu"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "../utils/global.cu"

//Incluyo la clase binariaCPU.cu para procesar las islas en CPU
#include "binariaCPU.cu"

//Incluyo la clase donde se calcula el fitness
#include "../fitness/binariaGenFitness.cu"

using namespace std;

/****************** Representacion binaria ********************/

/******************* Declaracion de Funciones del Host *************************/
void representacionBinariaGen();

void traerImprimirBinariaGen(float* fitnessHost, float* fitnessDevice, int tamVectorFitness, bool* poblacionHost, bool* poblacionDevice, int tamVectorPoblacion, int iteracion);

/******************* Declaracion de Funciones Global del Device ********************/

__global__ void inicializarPoblacionBinariaGen(bool* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice);
__global__ void mainKernelBinariaGen(bool* poblacionActualDevice, bool* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int*  nuevaPoblacionAcambiarDevice, float* probabilidadRuletaDevice, float* fitnessTotalParaRuletaDevice, struct Random r, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado);

//Estaas funciones son global porque es necesario sincronizar los bloques de cada poblacion antes de operar
__global__ void migracionRepresentacionBinariaGen(bool* poblacionActualDevice, float* fitnessActualDevice, bool* individuosAMigrarDevice, struct Random randomGPU, bool* individuoAMigrarCPUaGPUdevice);
__global__ void individosAMigrarRepresentacionBinariaGen(float* nuevoFitnessDevice, bool* nuevaPoblacionDevice, bool* individuosAMigrarDevice, bool * individuoAMigrarGPUaCPUdevice);

__global__ void reemplazoBinariaGen(bool* poblacionActualDevice, bool* nuevaPoblacionDevice, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActualDevice, float* nuevoFitnessDevice);

/******************* Declaracion de Funciones internas del Device *****************/
__device__ int obtenerMejorIndividuoBinariaGen(float* vectorFitness);
__device__ void obtenerPeorIndividuoBinariaGen(float* vectorFitness, int indices[2]);

__device__ void asignarProbabilidadRuletaBinariaGen(float* fitnessActualDevice, float* probabilidadRuletaBinaria, float* fitnessTotalParaRuletaDevice, struct Random randomGPU);

__device__ int seleccionBinariaGen(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado);
__device__ int seleccionPorTorneoBinariaGen(float* vectorFitness, struct Random randomGPU);
__device__ int seleccionPorRangoBinariaGen(int* vectorFitness, struct Random randomGPU);
__device__ int seleccionRuedaRuletaBinariaGen(float* probabilidadRuletaDevice, struct Random randomGPU);

__device__ void cruzamientoRepresentacionBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoCromosoma1PuntoBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosoma2PuntosBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosomaUniformeBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU);

__device__ void mutacionBinariaGen(bool* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void mutacionPorInversionGen(bool* poblacion, float probabilidad, struct Random randomGPU);

__device__ void asignarFitnessBinariaGen(bool* poblacion, float* vectorFitness, float* fitnessOrdenadoDevice);

__device__ void reemplazoElitistaBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness);

/******************* Implementacion de Funciones del Device *****************/

__global__ void inicializarPoblacionBinariaGen(bool* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice){
	//Obtengo posicion a escribir.
	int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	//Genero valor aleatorio.
	unsigned int r = (getRandomInt(randomGPU, posicionGen)% 2);
	bool valor = (r == 1);
	poblacion[posicionGen] = valor;
	asignarFitnessBinariaGen(poblacion, fitnessActualDevice, fitnessOrdenadoDevice);
}

__global__ void mainKernelBinariaGen(bool* poblacionActualDevice, bool* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int*  nuevaPoblacionAcambiarDevice, float* probabilidadRuletaDevice, float* fitnessTotalParaRuletaDevice, struct Random r, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado){
	if(tipoSeleccionDevice == 2){
		asignarProbabilidadRuletaBinariaGen(fitnessActualDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r);
		cuPrintf("total = %f\n", fitnessTotalParaRuletaDevice[0]);
		for(int i=0;i<cantCromosomasPoblacionDevice*cantidadPoblacionesGPUDevice;i++){
			cuPrintf("** probabilidadRuletaDevice ** i = %d prob = %f ",i, probabilidadRuletaDevice[i]);
		}
		cuPrintf("\n");
	}
	int posicionCromosoma1 = seleccionBinariaGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	int posicionCromosoma2 = seleccionBinariaGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	cuPrintf("posicionCromosoma1 = %d\n", posicionCromosoma1);
	cuPrintf("posicionCromosoma2 = %d\n", posicionCromosoma2);
	cruzamientoRepresentacionBinariaGen(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamientoDevice, r, tipoCruzamientoDevice);
	mutacionBinariaGen(nuevaPoblacionDevice, probabilidadMutacionDevice, r, tipoMutacionDevice);
	asignarFitnessBinariaGen(nuevaPoblacionDevice, nuevoFitnessDevice, fitnessOrdenadoDevice);
}


__device__ void asignarProbabilidadRuletaBinariaGen(float* fitnessActualDevice, float* probabilidadRuletaBinaria, float* fitnessTotalParaRuletaDevice, struct Random randomGPU){
	__syncthreads();
	if (((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0) && (threadIdx.y == 0)){
		fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] = 0;
		for (int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++) {
			fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] += fitnessActualDevice[i];
		}
	}
	if ((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0 && threadIdx.y == 0){
		for (int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++) {
			probabilidadRuletaBinaria[i] = fitnessActualDevice[i]/fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] * 100;
		}
	}

}


/******************* Implementacion de Funciones de Migracion *****************/

//Obtiene el mejor individuo de una poblacion (1 bloque por poblacion)
//selecciona el mejor elemento
__device__ int obtenerMejorIndividuoBinariaGen(float* vectorFitness){
	float mejor = INT_MIN;
	int indiceMejor;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor) {
			mejor = valorFitness;
			indiceMejor = i;
		}
	}
	int posicionCromosomaMejor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor * cantGenesEnCromosomaDevice;
	return posicionCromosomaMejor;
}

//Obtiene el indice del peor individuo y del fitness del peor individuo de una poblacion (1 bloque por poblacion) y retorna por referencia en el arreglo indices
__device__ void obtenerPeorIndividuoBinariaGen(float* vectorFitness, int indices[2]){
	float peor = INT_MAX;
	int indicePeor;
	float valorFitness;
	int posicionCromosomaPeor;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness < peor) {
			peor = valorFitness;
			indicePeor = i;
		}
	}
	posicionCromosomaPeor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indicePeor * cantGenesEnCromosomaDevice;
	indices[0] = posicionCromosomaPeor;
	indices[1] = indicePeor;
}

__global__ void migracionRepresentacionBinariaGen(bool* poblacionActualDevice, float* fitnessActualDevice, bool* individuosAMigrarDevice, struct Random randomGPU, bool* individuoAMigrarCPUaGPUdevice){
	int indices[2];
	obtenerPeorIndividuoBinariaGen(fitnessActualDevice, indices);
	int posicionCromosomaEmigrante = indices[0];
	int posicionCromosomaInmigrante;
	int aux;

	if (blockIdx.y == 0) {
		aux = cantidadPoblacionesGPUDevice - 1;
	} else {
		aux = (blockIdx.y - 1) % cantidadPoblacionesGPUDevice;
	}
	posicionCromosomaInmigrante = aux * cantGenesEnCromosomaDevice;

	if(blockIdx.y == 0 && cantidadPoblacionesCPUDevice > 0){
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			poblacionActualDevice[posicionCromosomaEmigrante + i] = individuoAMigrarCPUaGPUdevice[i];
		}
	} else {
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			poblacionActualDevice[posicionCromosomaEmigrante + i] = individuosAMigrarDevice[posicionCromosomaInmigrante + i];
		}
	}

	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice + indices[1];
	fitnessActualDevice[posicionFitness] = calcularFitnessBinariaGen(poblacionActualDevice, posicionCromosomaEmigrante);

	cuPrintf("posicionCromosomaInmigrante = %d \n", posicionCromosomaInmigrante);
	cuPrintf("posicionCromosomaEmigrante = %d \n", posicionCromosomaEmigrante);
	cuPrintf("posicionFitness = %d \n", posicionFitness);
}

__global__ void individosAMigrarRepresentacionBinariaGen(float* nuevoFitnessDevice, bool* nuevaPoblacionDevice, bool* individuosAMigrarDevice, bool * individuoAMigrarGPUaCPUdevice){
	int posicionMejorIndividuo = obtenerMejorIndividuoBinariaGen(nuevoFitnessDevice);
	if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice - 1){
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			individuoAMigrarGPUaCPUdevice[i] = nuevaPoblacionDevice[posicionMejorIndividuo + i];
		}
	}else{
		for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
			individuosAMigrarDevice[blockIdx.y*cantGenesEnCromosomaDevice + i] = nuevaPoblacionDevice[posicionMejorIndividuo + i];
		}
	}
}


/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionBinariaGen(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado){
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionPorRangoBinariaGen(vectorIndicesFitnessOrdenado, randomGPU);
	} else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoBinariaGen(vectorFitness, randomGPU);
		} else {
			if(ejecutarFuncion == 2) {
				resultado = seleccionRuedaRuletaBinariaGen(probabilidadRuletaDevice, randomGPU);
			}
		}
	}
	return resultado;
}


//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoBinariaGen(float* vectorFitness, struct Random randomGPU){
	//cuPrintf("*** Seleccion por torneo ***\n");
	unsigned int r = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice) % cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * 2 + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + r * cantGenesEnCromosomaDevice;

	if (blockIdx.y % cantCromosomasPoblacionDevice != 0) {
		posicionFitnessAleatoria1 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) + r;
		posicionCromosomaAleatoria1 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) * cantGenesEnCromosomaDevice * 2  + r * cantGenesEnCromosomaDevice;
	}

	r = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice + 1) % cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * 2 + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantGenesEnCromosomaDevice * 2 + r * cantGenesEnCromosomaDevice;

	if (blockIdx.y % cantCromosomasPoblacionDevice != 0) {
		posicionFitnessAleatoria2 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) + r;
		posicionCromosomaAleatoria2 = (blockIdx.y - (blockIdx.y % cantCromosomasPoblacionDevice)) * cantGenesEnCromosomaDevice * 2  + r * cantGenesEnCromosomaDevice;
	}

	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}

//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionPorRangoBinariaGen(int* vectorIndicesFitnessOrdenado, struct Random randomGPU){
	//cuPrintf("*** Seleccion por rango ***\n");

	unsigned int indiceMejor = getRandomInt(randomGPU, blockIdx.y/cantGenesEnCromosomaDevice) % cantidadIndividuosSeleccionPorRangoDevice;
	int indiceFitness = (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice + indiceMejor;

	int inicioPoblacion = (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	//cuPrintf("indMejor = %d, iniPob = %d, vector[indiceFitness] = %d, vector[indiceFitness] * cantGenesEnCromosomaDevice = %d\n", indiceMejor, inicioPoblacion, vectorIndicesFitnessOrdenado[indiceFitness], vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice);
	//cuPrintf("blockIdx.y/cantGenesEnCromosomaDevice = %d\n", blockIdx.y/cantGenesEnCromosomaDevice);
	cuPrintf("indiceMejor = %d\n", indiceMejor);
	cuPrintf("vectorIndicesFitnessOrdenado[%d] = %d\n", indiceFitness, vectorIndicesFitnessOrdenado[indiceFitness]);
	int posicionCromosoma = inicioPoblacion + vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice;
	return posicionCromosoma;
}

//Seleccion por rueda de ruleta
__device__ int seleccionRuedaRuletaBinariaGen(float* probabilidadRuletaDevice, struct Random randomGPU){
	float aux = getRandomInt(randomGPU, blockIdx.y*cantGenesEnCromosomaDevice);
	float r = fmod(aux, 100);
	float suma = probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice];
	int i = 0;
	while(r>suma){
		i++;
		suma = suma + probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice + i];
	}
	return (blockIdx.y / (cantCromosomasPoblacionDevice / 2) ) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + i * cantGenesEnCromosomaDevice;
}

/******************* Implementacion de Funciones de Cruzamiento *****************/

__device__ void cruzamientoRepresentacionBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoCromosoma1PuntoBinariaGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	} else{
		if(ejecutarFuncion == 1){
			cruzamientoCromosoma2PuntosBinariaGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		} else {
			if(ejecutarFuncion == 2) {
				cruzamientoCromosomaUniformeBinariaGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
		}
	}
}


//////////////// Cruzamiento representacion binaria /////////////////////
//Se selecciona un punto de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma1PuntoBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Cruzamiento 1Punto ***\n");
	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);
	unsigned int r = (getRandomInt(randomGPU, blockIdx.y) % cantGenesEnCromosomaDevice);

	if (prob < probabilidad){
		if ((threadIdx.y < cantGenesEnCromosomaDevice) && (threadIdx.y < r)){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else if ((threadIdx.y - cantGenesEnCromosomaDevice) < r){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}else{
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}else{
		nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionNuevoGen];
	}
}

//Se seleccionan 2 puntos de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma2PuntosBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Cruzamiento 2Puntos ***\n");

	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, blockIdx.y) % 101);
	unsigned int r1 = (getRandomInt(randomGPU, blockIdx.y) % cantGenesEnCromosomaDevice);
	unsigned int r2 = (getRandomInt(randomGPU, blockIdx.y + r1) % cantGenesEnCromosomaDevice);
	unsigned int r3;

	if (r2 < r1){
		r3 = r2;
		r2 = r1;
		r1 = r3;
	}

	if (prob < probabilidad){
		//cuPrintf("r1 = %d\n", r1);
		//cuPrintf("r2 = %d\n", r2);
		if ((threadIdx.y < cantGenesEnCromosomaDevice) && ((threadIdx.y < r1)||(threadIdx.y > r2))){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else if (((threadIdx.y - cantGenesEnCromosomaDevice) < r1) || ((threadIdx.y - cantGenesEnCromosomaDevice) > r2)){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}else{
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}else{
		nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionNuevoGen];
	}
}


//en cada posición de los progenitores se decide intercambiar el valor
//genético de cada punto de acuerdo a una probabilidad prefijada
__device__ void cruzamientoCromosomaUniformeBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Cruzamiento Uniforme ***\n");

	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int prob = (getRandomInt(randomGPU, threadIdx.y % cantGenesEnCromosomaDevice) % 101);
	cuPrintf("prob cruzamiento = %d\n", prob);
	if (prob < probabilidad){
		if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y];
		}else{
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}else{
		if(threadIdx.y < cantGenesEnCromosomaDevice){
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma1 + threadIdx.y];
		}else{
			nuevaPoblacion[posicionNuevoGen] = poblacionActual[posicionCromosoma2 + threadIdx.y - cantGenesEnCromosomaDevice];
		}
	}
}

/******************* Implementacion de Funciones de Mutacion *****************/

__device__ void mutacionBinariaGen(bool* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion){
	if(ejecutarFuncion == 0) {
		mutacionPorInversionGen(poblacion, probabilidad, randomGPU);
	}
}


//Mutacion por inversion del valor del alelo
//Se modifica aleatoriamente (con probabilidad ''probabilidad') uno de los valores binarios del cromosoma
__device__ void mutacionPorInversionGen(bool* poblacion, float probabilidad, struct Random randomGPU){
	//cuPrintf("*** Mutacion por inversion ***\n");
	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
	unsigned int bloque = blockIdx.y;
	unsigned int r;

	if (threadIdx.y >= cantGenesEnCromosomaDevice){
		bloque++;
	}
	r = (getRandomInt(randomGPU, bloque) % cantGenesEnCromosomaDevice);

	int prob = (getRandomInt(randomGPU, bloque) % 101);
	if (prob < probabilidad) {
		if((threadIdx.y % cantGenesEnCromosomaDevice) == r)
			poblacion[posicionNuevoGen] = (poblacion[posicionNuevoGen] + 1) % 2;
	}
}

/******************* Implementacion de Funciones de fitness *****************/

__device__ void asignarFitnessBinariaGen(bool* poblacion, float* vectorFitness, float* fitnessOrdenadoDevice){
	__syncthreads();
	if (threadIdx.y % cantGenesEnCromosomaDevice == 0){
		int posicionGen = blockIdx.y * cantGenesEnCromosomaDevice * 2 + threadIdx.y;
		int posicionFitness = blockIdx.y*2 + threadIdx.y/cantGenesEnCromosomaDevice;
		vectorFitness[posicionFitness] = 0;
		for (int i = posicionGen; i < posicionGen + cantGenesEnCromosomaDevice; i++) {
			vectorFitness[posicionFitness] = vectorFitness[posicionFitness] + (float)poblacion[i];
			fitnessOrdenadoDevice[posicionFitness] = vectorFitness[posicionFitness];
		}
	}
}


/******************** Implementacion de Funciones de reemplazo **************/

__global__ void reemplazoBinariaGen(bool * poblacionActualDevice, bool * nuevaPoblacionDevice, int * poblacionActualAcambiar, int * nuevaPoblacionAcambiar, float * fitnessActualDevice, float* nuevoFitnessDevice){
	if(tipoReemplazoDevice == 1) {
		reemplazoElitistaBinariaGen(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiar, nuevaPoblacionAcambiar, fitnessActualDevice, nuevoFitnessDevice);
	}
}

__device__ void reemplazoElitistaBinariaGen(bool* poblacionActual, bool* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness) {
	//si es el primer bloque de alguna poblacion y es el primer hilo de ese bloque entonces reemplazo
	if (((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0) && (threadIdx.y == 0)){
		int cantAremplazar = porcentajeElitismoDevice*cantCromosomasPoblacionDevice/100;
		int peor = INT_MAX;
		int mejor = INT_MIN;
		int posPeor = -1;
		int posMejor = -1;
		int posFitness = blockIdx.y / 2 * cantCromosomasPoblacionDevice;
		//este for se encarga de inicializar los primeros cantAreemplazar valores de la estructura auxiliar poblacionActualAcambiar en 1 y guarda su peor valor para luego poder obtener los peores canAReemplazar valores
		//tambien inicializa los primeros canAreemplazar valores de nuevaPoblacionACambiar en 1 y guarda su mejor valor
		for (int i = 0; i < cantAremplazar; i++){
			if (fitnessActual[posFitness + i] < peor) {
				peor = fitnessActual[posFitness + i];
				posPeor = i;
			}

			if (nuevoFitness[posFitness + i] > mejor) {
				mejor = nuevoFitness[posFitness + i];
				posMejor = i;
			}

			poblacionActualAcambiar[posFitness + i] = 1;
			nuevaPoblacionAcambiar[posFitness + i] = 1;
		}

		//esta iteracion se encarga de obtener los cantAremplazar peores valores de la nueva poblacion y los mejores cantAremplazar valores de la poblacion actual
		for (int i = cantAremplazar; i < cantCromosomasPoblacionDevice; i++){
			if (fitnessActual[posFitness + i] > peor) {
				poblacionActualAcambiar[posFitness + i] = 1;
				poblacionActualAcambiar[posFitness + posPeor] = 0;
				peor = INT_MAX;
				posPeor = -1;
				for (int j = 0; j <= i; j++){
					if (poblacionActualAcambiar[posFitness + j] == 1) {
						if (fitnessActual[posFitness + j] < peor) {
							peor = fitnessActual[posFitness + j];
							posPeor = j;
						}
					}
				}
			}
			else {
				poblacionActualAcambiar[posFitness + i] = 0;
			}

			if (nuevoFitness[posFitness + i] < mejor) {
				nuevaPoblacionAcambiar[posFitness + i] = 1;
				nuevaPoblacionAcambiar[posFitness + posMejor] = 0;
				mejor = INT_MIN;
				posMejor = -1;
				for (int j = 0; j <= i; j++) {
					if (nuevaPoblacionAcambiar[posFitness + j] == 1) {
						if (nuevoFitness[posFitness + j] > mejor) {
							mejor = nuevoFitness[posFitness + j];
							posMejor = j;
						}
					}
				}
			} else {
				nuevaPoblacionAcambiar[posFitness + i] = 0;
			}
		}

		for (int i = 0; i < cantCromosomasPoblacionDevice; i++){
			cuPrintf("poblacionActualAcambiar[%d] = %d\n",i,poblacionActualAcambiar[i]);
			//cuPrintf("nuevaPoblacionAcambiar[%d] = %d\n",i,nuevaPoblacionAcambiar[i]);
		}
		for (int i = 0; i < cantCromosomasPoblacionDevice; i++){
			//cuPrintf("poblacionActualAcambiar[%d] = %d\n",i,poblacionActualAcambiar[i]);
			cuPrintf("nuevaPoblacionAcambiar[%d] = %d\n",i,nuevaPoblacionAcambiar[i]);
		}

		//esta iteracion se encarga de reemplazar los mejores cantAremplazar individuos de la poblacion actual por los cantAremplazar peores individuos de la nueva poblacion
		for (int i = 0; i < cantAremplazar; i++) {
			int j = 0;
			while (poblacionActualAcambiar[posFitness + j] == 0) {
				j++;
			}

			int k = 0;
			while (nuevaPoblacionAcambiar[posFitness + k] == 0) {
				k++;
			}

			nuevoFitness[posFitness + k] = fitnessActual[posFitness + j];
			int posNuevoCromosoma = blockIdx.y * 2 * cantGenesEnCromosomaDevice + k * cantGenesEnCromosomaDevice;
			int posCromosomaActual = blockIdx.y * 2 * cantGenesEnCromosomaDevice + j * cantGenesEnCromosomaDevice;
			for (int h = 0; h < cantGenesEnCromosomaDevice; h++) {
				nuevaPoblacion[posNuevoCromosoma + h] = poblacionActual[posCromosomaActual + h];
			}
			poblacionActualAcambiar[posFitness + j] = 0;
			nuevaPoblacionAcambiar[posFitness + k] = 0;
		}
	}
}

/******************* Implementacion de Funciones del host *****************/

void traerImprimirBinariaGen(float* fitnessHost, float* fitnessDevice, int tamVectorFitness, bool* poblacionHost, bool* poblacionDevice, int tamVectorPoblacion, int iteracion){
	cudaMemcpy (fitnessHost, fitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionHost, poblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "\nPOBLACION ITERACION "<< iteracion<<" GPU\n";
	for (int i = 0; i<totalGenesGPU; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%d ", poblacionHost[i]);
	}
	cout << "\n\n\nFITNESS GPU\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
		printf("%f ", fitnessHost[i]);
	}
	cout << "\n\n\n\n";
}

void representacionBinariaGen() {

	int tamVectorPoblacion = sizeof(int)*totalGenesGPU;
	int tamVectorFitness = sizeof(float)*totalIndividuosGPU;
	int tamVectorIndividuosAMigrarGPU = sizeof(int)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorIndividuosAMigrarCPU = sizeof(int)*cantGenesEnCromosoma*cantidadPoblacionesCPU;

	int tamVectorPoblacionParaRuleta = sizeof(float)*totalIndividuosGPU;
	int tamVectorFitnessFloat = sizeof(float)*cantidadPoblacionesGPU;
	int tamVectorPoblacionParaRuletaCPU = sizeof(float)*totalIndividuosCPU;
	int tamVectorFitnessFloatCPU = sizeof(float)*cantidadPoblacionesCPU;

	static bool *poblacionActualHostGPU = (bool *) malloc(tamVectorPoblacion);
	static bool *nuevaPoblacionHostGPU = (bool *) malloc(tamVectorPoblacion);
	static float *fitnessActualHostGPU = (float *) malloc(tamVectorFitness);
	static float *nuevoFitnessHostGPU = (float *) malloc(tamVectorFitness);
	static float *probabilidadRuletaHost = (float *) malloc(tamVectorPoblacionParaRuleta);
	static float *fitnessTotalParaRuletaHost = (float *) malloc(tamVectorFitnessFloat);
	static bool *individuosAMigrarHostGPU = (bool *) malloc(tamVectorIndividuosAMigrarGPU);
//SE BORRA//
	static int *indicesFitnessOrdenadoHost = (int *) malloc(tamVectorFitness);
////////////

	bool *poblacionActualDevice;
	bool *nuevaPoblacionDevice;
	float *fitnessActualDevice;
	float *nuevoFitnessDevice;
	float * fitnessOrdenadoDevice;
	int * indicesFitnessOrdenadoDevice;
	float *probabilidadRuletaDevice;
	float *fitnessTotalParaRuletaDevice;
	int * poblacionActualAcambiarDevice;
	int * nuevaPoblacionAcambiarDevice;
	bool *individuosAMigrarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&indicesFitnessOrdenadoDevice, tamVectorFitness);
	cudaMalloc(&fitnessOrdenadoDevice, tamVectorFitness);
	cudaMalloc(&probabilidadRuletaDevice, tamVectorPoblacionParaRuleta);
	cudaMalloc(&fitnessTotalParaRuletaDevice, tamVectorFitnessFloat);
	cudaMalloc(&poblacionActualAcambiarDevice, tamVectorFitness);
	cudaMalloc(&nuevaPoblacionAcambiarDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion*cantGenesEnCromosoma*cantidadPoblacionesGPU);

	//inicializo grid con dimension 1 x cantidadPoblacionesGPU*cantCromosomasPoblacion/2 bloques
	dim3 tamGrid(1, cantidadPoblacionesGPU*cantCromosomasPoblacion/2); //Grid dimensión
	///inicializo bloque con dimension 1 x cantGenesEnCromosoma*2 x 1 hilos
	dim3 tamBlock(1,cantGenesEnCromosoma*2,1); //Block dimensión

	//inicializo grid de individuos a migrar con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridIndividuosAMigrar(1, cantidadPoblacionesGPU);
	//inicializo bloque de individuos a migrar con dimension 1 x cantCromosomasPoblacion/2 x 1 hilos
	dim3 tamBlockIndividuosAMigrar(1,cantCromosomasPoblacion/2,1);

	//inicializo grid de migracion con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridMigracion(1, cantidadPoblacionesGPU);
	//inicializo bloque de migracion con 1 hilo por bloque
	dim3 tamBlockMigracion(1,1,1); //Block dimensión


	//inicializacion de la poblacion en GPU
	if(cantidadPoblacionesGPU > 0){
		inicializarPoblacionBinariaGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice);

		//si la selección es por rango
		if (tipoSeleccion == 0) {
			ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
		}
		traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, -1);
	}

	static bool *individuosAMigrarHostCPU;
	MersenneTwister* mt;
	int tamVectorPoblacionCPU;
	int tamVectorFitnessCPU;
	static bool *poblacionActualHostCPU;
	static bool *nuevaPoblacionHostCPU;
	static float *fitnessActualHostCPU;
	static float *nuevoFitnessHostCPU;
	static int *indicesFitnessOrdenadoHostCPU;
	static float * fitnessOrdenadoHostCPU;
	int * poblacionActualAcambiarHostCPU;
	int * nuevaPoblacionAcambiarHostCPU;
	float * probabilidadRuletaHostCPU;
	float * fitnessTotalParaRuletaHostCPU;

	if(cantidadPoblacionesCPU > 0){

		tamVectorPoblacionCPU = sizeof(int)*totalGenesCPU;
		tamVectorFitnessCPU = sizeof(int)*totalIndividuosCPU;

		poblacionActualHostCPU = (bool *) malloc(tamVectorPoblacionCPU);
		nuevaPoblacionHostCPU = (bool *) malloc(tamVectorPoblacionCPU);
		fitnessActualHostCPU = (float *) malloc(tamVectorFitnessCPU);
		nuevoFitnessHostCPU = (float *) malloc(tamVectorFitnessCPU);
		individuosAMigrarHostCPU = (bool *) malloc(tamVectorIndividuosAMigrarCPU);
		indicesFitnessOrdenadoHostCPU = (int *) malloc(tamVectorFitness);
		fitnessOrdenadoHostCPU = (float *) malloc(tamVectorFitness);
		poblacionActualAcambiarHostCPU = (int *) malloc(tamVectorFitness);
		nuevaPoblacionAcambiarHostCPU = (int *) malloc(tamVectorFitness);
		probabilidadRuletaHostCPU = (float *) malloc(tamVectorPoblacionParaRuletaCPU);
		fitnessTotalParaRuletaHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);

		//////////INICIALIZO POBLACION EN CPU
		//inicializo la clase mt para las variables aleatorias

		//Inicio numeros aleatorios.
		mt = new MersenneTwister();
		mt->init_genrand(time(NULL));

		//inicializacion de la poblacion en GPU
		inicializarPoblacionBinariaCPU(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU);
		imprimirBinariaCPU(fitnessActualHostCPU, tamVectorFitnessCPU, poblacionActualHostCPU, tamVectorPoblacionCPU, -1);

		//si la selección es por rango
		if (tipoSeleccion == 0) {
			for (int i = 0; i < cantidadPoblacionesCPU; i++) {
				ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
			}
		}
	}

	int tamVectorIndividuoAMigrarCPUGPU;
	static bool *individuoAMigrarCPUaGPUhost;
	static bool *individuoAMigrarGPUaCPUhost;
	static bool *individuoAMigrarCPUaGPUdevice;
	static bool *individuoAMigrarGPUaCPUdevice;
	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		tamVectorIndividuoAMigrarCPUGPU = sizeof(int)*cantGenesEnCromosoma;
		cudaMalloc(&individuoAMigrarCPUaGPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		cudaMalloc(&individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarCPUaGPUhost = (bool *) malloc(tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarGPUaCPUhost = (bool *) malloc(tamVectorIndividuoAMigrarCPUGPU);
	}

	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){
		if(cantidadPoblacionesGPU > 0){
			//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
			if ((iteracion % 2) == 0) {
			
				if ((tipoSeleccion == 0) && (modoDebug == 1)) {
					cudaMemcpy (indicesFitnessOrdenadoHost, indicesFitnessOrdenadoDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
					cout << "\n\n\nINDICES GPU\n";
					for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
						printf("%d ", indicesFitnessOrdenadoHost[i]);
					}
					cout << "\n\n\n\n";
				}
				
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelBinariaGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				
				//traerImprimirBinariaGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, 222);

				reemplazoBinariaGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, fitnessActualDevice, nuevoFitnessDevice);

				//si la selección es por rango
				if (tipoSeleccion == 0) {
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}

				if (modoDebug == 1) {
					traerImprimirBinariaGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
				}
			} else { //sino invoco a GPU con nuevaPoblacion, poblacionActual
				
				if ((tipoSeleccion == 0) && (modoDebug == 1)) {
					cudaMemcpy (indicesFitnessOrdenadoHost, indicesFitnessOrdenadoDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
					cout << "\n\n\nINDICES GPU\n";
					for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
						printf("%d ", indicesFitnessOrdenadoHost[i]);
					}
					cout << "\n\n\n\n";
				}
				
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelBinariaGen<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				
				//traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, 333);

				reemplazoBinariaGen<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, nuevoFitnessDevice, fitnessActualDevice);
				//si la selección es por rango
				if (tipoSeleccion == 0) {
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}

				if (modoDebug == 1) {
					traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
				}
			}
		}

		//Tiro islas en CPU
		if(cantidadPoblacionesCPU > 0) {
			if ((tipoSeleccion == 0) && (modoDebug == 1)) {
				cout << "\n\n\nINDICES CPU\n";
				for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesCPU; i++){
					printf("%d ", indicesFitnessOrdenadoHostCPU[i]);
				}
				cout << "\n\n\n\n";
			}
			representacionBinariaCPU(poblacionActualHostCPU, nuevaPoblacionHostCPU, fitnessActualHostCPU, nuevoFitnessHostCPU, poblacionActualAcambiarHostCPU,nuevaPoblacionAcambiarHostCPU, individuosAMigrarHostCPU, mt, iteracion, tamVectorPoblacion, tamVectorFitness, individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU);

			//si la selección es por rango
			if (tipoSeleccion == 0) {
				for (int i = 0; i < cantidadPoblacionesCPU; i++) {
					ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
				}
			}
		}

		//Si hay que migrar
		if ((iteracion + 1) % generacionesMigrar == 0) {

			if(cantidadPoblacionesGPU > 0){
				if ((iteracion % 2) == 0) {
					individosAMigrarRepresentacionBinariaGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				} else {
					individosAMigrarRepresentacionBinariaGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(fitnessActualDevice, poblacionActualDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				}
			}

			if(cantidadPoblacionesGPU > 0 && cantidadPoblacionesCPU > 0){
				cudaMemcpy (individuoAMigrarCPUaGPUdevice, individuoAMigrarCPUaGPUhost, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyHostToDevice);
				cudaMemcpy (individuoAMigrarGPUaCPUhost, individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyDeviceToHost);

				if (modoDebug == 1) {
					/**  Imprimir individuos a migrar **/
					cudaMemcpy (individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("----individuosAMigrar GPU----\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++) {
						printf("%d", individuosAMigrarHostGPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n---------------------------\n");


					printf("----individuo a migrar de GPU a CPU----\n");
					for (int i = 0; i<cantGenesEnCromosoma; i++) {
						printf("%d", individuoAMigrarGPUaCPUhost[i]);
					}
					printf("\n-------------------------------------\n");

					printf("________individuosAMigrar CPU______\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++) {
						printf("%d", individuosAMigrarHostCPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n__________________________________\n");

					printf("_____individuo a migrar de CPU a GPU_____\n");
					for (int i = 0; i<cantGenesEnCromosoma; i++) {
						printf("%d", individuoAMigrarCPUaGPUhost[i]);
					}
					printf("\n________________________________________\n");

					/** ************* **/
				}

				if ((iteracion % 2) == 0) {
					migracionRepresentacionBinariaGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n**Despues de Migrar GPU**";
						traerImprimirBinariaGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					}
					migracionRepresentacionBinariaCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirBinariaCPU(nuevoFitnessHostCPU, tamVectorFitness, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionBinariaGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n**Despues de Migrar GPU**";
						traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					}
					migracionRepresentacionBinariaCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirBinariaCPU(fitnessActualHostCPU, tamVectorFitness, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
					}
				}
			}else if(cantidadPoblacionesCPU > 0){
				//poblaciones solamente en CPU
				if ((iteracion % 2) == 0) {
					migracionRepresentacionBinariaCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirBinariaCPU(nuevoFitnessHostCPU, tamVectorFitness, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionBinariaCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar CPU **";
						imprimirBinariaCPU(fitnessActualHostCPU, tamVectorFitness, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
					}
				}
				if (modoDebug == 1) {
					printf("individuosAMigrar CPU\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++) {
						printf("%d", individuosAMigrarHostCPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n-----------------\n");
				}
			} else {
				//poblaciones solamente en GPU
				if (modoDebug == 1) {
					/**  Imprimir individuos a migrar **/
					cudaMemcpy (individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("individuosAMigrar GPU\n");
					for (int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++) {
						printf("%d", individuosAMigrarHostGPU[i]);
						if ((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n-----------------\n");
					/** ************* **/
				}
				if ((iteracion % 2) == 0) {
					migracionRepresentacionBinariaGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar **";
						traerImprimirBinariaGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					}
				} else {
					migracionRepresentacionBinariaGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if (modoDebug == 1) {
						cout <<"\n** Despues de Migrar **";
						traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					}
				}
			}
		}
		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	if (cantidadPoblacionesGPU > 0) {
		if (((iteracion -1) % 2) == 0) {
			traerImprimirBinariaGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitness, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion,INT_MAX);
		} else {
			traerImprimirBinariaGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitness, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion,INT_MAX);
		}
	}
	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHostGPU);
	cudaFree(nuevaPoblacionDevice);
}
